﻿using OpenTK.Graphics;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using EventArgs = System.EventArgs;
using OpenTK;
using LightCAD.Three;
using LightCAD.Three.OpenGL;
using LightCAD.MathLib;
using System.Runtime.InteropServices;
using System.Security;
using LightCAD.Model;
using LightCAD.Runtime;
using WeifenLuo.WinFormsUI.Docking;
using Avalonia.Controls;
using LightCAD.ComEditor;

namespace LightCAD.UI
{
    public partial class ComponentEditWindow : DockContent, IGLControl, IComponentEditControl
    {
        private GLControl glControl;

        internal event Action onInit;
        internal event Action onDestory;
        internal event Action<string, MouseEventRuntime> mouseEvent;
        internal event Action<KeyEventRuntime> keyDown;
        internal event Action<KeyEventRuntime> keyUp;
        internal event Action<Bounds> sizeChanged;
        internal event Action onRender;


        private WebGLRenderer renderer;
        private PerspectiveCamera camera;
        private Scene scene;
        private Group model;
        private BoxGeometry geometry;
        private RenderCanvas canvas;
        private ComEditor.OrbitControls controls;

        private ComponentEditRuntime EditRt;
        public ComponentEditWindow()
        {
            InitializeComponent();
            this.glControl = new GLControl();

            this.glControl.VSync = false;
            this.spContainer.Panel2.Controls.Add(this.glControl);
            this.glControl.Dock = DockStyle.Fill;
            this.Load += MainForm_Load;
            this.Closing += MainForm_Closing;
            this.glControl.Paint += GlControl_Paint;
            this.WindowState = FormWindowState.Maximized;
            //glControl.Run();
            //Application.Idle += Application_Idle;

            this.EditRt = new ComponentEditRuntime(this);
            this.EditRt.CmdHandler.Initilize();
            this.EditRt.CmdHandler.SetActive();
        }



        private bool inRender;
        private bool isLoad = false;

        public Bounds EditorBounds => glControl.Bounds.ToRuntime();

        ICommandControl IEditControl.CommandBar => this.CommandBar;



        //V3.V(SuppressUnmanagedCodeSecurity]
        //V3.V(DllImport("OPENGL32.DLL", EntryPoint = "wglGetProcAddress", ExactSpelling = true, SetLastError = true)]
        //internal extern static IntPtr GetProcAddress(String lpszProc);
        [SuppressUnmanagedCodeSecurity]
        [DllImport("OPENGL32.DLL", EntryPoint = "wglGetProcAddress", ExactSpelling = true, SetLastError = true)]
        internal extern static IntPtr GetProcAddress(IntPtr lpszProc);

        private void MainForm_Load(object? sender, EventArgs e)
        {
            var context = GraphicsContext.CurrentContext;
            gl.GetAddressFunc = (fn) =>
            {
                var context_internal = context as IGraphicsContextInternal;
                var ptr = context_internal.GetAddress(fn);
                return ptr;
            };


            init();
            Resize();
            this.SizeChanged += MainForm_SizeChanged;
            isLoad = true;

        }
        private void GlControl_Paint(object? sender, PaintEventArgs e)
        {
            //MainUIWindow Closing时会从窗口向子元素清理资源，导致GLControl刷新异常
            if (AppRuntime.IsClosing) return;

            if (inRender) return;
            this.inRender = true;

            if (this.glControl.IsDisposed) return;
            try
            {
                this.glControl.MakeCurrent();

                //if (ShowFps) CalFPS();

                this.onRender?.Invoke();

                this.glControl.SwapBuffers();

            }
            catch (Exception ex)
            {
            }
            this.inRender = false;
        }

        private void Application_Idle(object? sender, EventArgs e)
        {
            this.glControl.MakeCurrent();
            Animate();
            this.glControl.SwapBuffers();
        }
        private void Animate()
        {
            if (!isLoad)
                return;
            this.renderer.setClearColor(0x000000);
            this.renderer.render(this.scene, camera);
        }

        private void MainForm_SizeChanged(object? sender, EventArgs e)
        {
            Resize();
        }
        private void Resize()
        {
            this.camera.aspect = this.Width / (float)this.Height;
            camera.updateProjectionMatrix();
            glControl.Width = this.Width;
            glControl.Height = this.Height;
            this.canvas.Set(glControl.Width, glControl.Height);
            this.renderer.setViewport(0, 0, glControl.Width, glControl.Height);
        }

        private void MainForm_Closing(object? sender, CancelEventArgs e)
        {
        }

        private void init()
        {
            this.scene = new Scene();
            var ambientLight = new AmbientLight(0xcccccc, 0.4);
            scene.add(ambientLight);

            var dirLight = new DirectionalLight(0xffffff, 0.8);
            dirLight.position.Set(1, 2, 3);
            scene.add(dirLight);

            this.geometry = new BoxGeometry(200, 200, 200);
            //var material = new MeshBasicMaterial(new MaterialValues { color = new LightCAD.Three.Color(0xff0000) });
            var material = new MeshPhongMaterial { color = new Color(0xff0000), side = LightCAD.MathLib.Constants.DoubleSide };//map = map,

            //var mesh = new LightCAD.Three.Mesh(geometry, material);
            //this.scene.add(mesh);

            this.model = new Group();
            this.scene.add(this.model);

            var ratio = glControl.Width / glControl.Height;
            camera = new PerspectiveCamera(70, ratio, 0.1, 1000);
            camera.position.Z = 300;

            this.canvas = new RenderCanvas().Set(glControl.Width, glControl.Height);
            this.renderer = new WebGLRenderer(canvas);
            //renderer.setSize(glControl.Width, glControl.Height);
            controls = new ComEditor.OrbitControls(camera, this);
            controls.screenSpacePanning = true;


        }
        private void tsiEditMode_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

        public void RefreshControl()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(() => { this.GlControl_Paint(null, null); });
            }
            else
            {
                this.GlControl_Paint(null, null);
            }
        }
        public void AttachEvents(Action onInit, Action onDestory,
          Action<string, MouseEventRuntime> mouseEvent,
          Action<KeyEventRuntime> keyDown,
          Action<KeyEventRuntime> keyUp,
          Action<Bounds> sizeChanged,
          Action onRender)
        {
            if (onInit != null) this.onInit += onInit;
            if (onDestory != null) this.onDestory += onDestory;
            if (mouseEvent != null) this.mouseEvent += mouseEvent;
            if (keyDown != null) this.keyDown += keyDown;
            if (keyUp != null) this.keyUp += keyUp;
            if (sizeChanged != null) this.sizeChanged += sizeChanged;
            if (onRender != null) this.onRender += onRender;
        }

        public void DetachEvents(Action onInit, Action onDestory,
            Action<string, MouseEventRuntime> mouseEvent,
            Action<KeyEventRuntime> keyDown,
             Action<KeyEventRuntime> keyUp,
           Action<Bounds> sizeChanged,
            Action onRender)
        {
            if (onInit != null) this.onInit -= onInit;
            if (onDestory != null) this.onDestory -= onDestory;
            if (mouseEvent != null) this.mouseEvent -= mouseEvent;
            if (keyDown != null) this.keyDown -= keyDown;
            if (keyUp != null) this.keyUp -= keyDown;
            if (sizeChanged != null) this.sizeChanged -= sizeChanged;
            if (onRender != null) this.onRender -= onRender;
        }
    }
}
